Tarea: Creación de un Modelo de Clasificación para Predecir Problemas en Procesos de un Servidor¶
Objetivo¶
Desarrollar un modelo de clasificación que pueda predecir si un proceso en ejecución en un servidor causará problemas, basándose en datos históricos de los procesos. Este modelo ayudará en la detección temprana de procesos potencialmente problemáticos, permitiendo tomar acciones preventivas para asegurar la estabilidad y el rendimiento del servidor.
Datos¶
El conjunto de datos proporcionado incluye las siguientes características para cada proceso en el servidor:
ID_Proceso: Identificador único del proceso.Uso_CPU: Porcentaje del CPU utilizado por el proceso.Uso_Memoria: Porcentaje de memoria utilizada por el proceso.Numero_Hilos: Número de hilos del proceso.Tiempo_Ejecucion: Tiempo de ejecución del proceso en horas.Numero_Errores: Número de errores generados por el proceso en las últimas 24 horas.Tipo_Proceso: Categoría del proceso (Servicio, Aplicación, Sistema).
La variable objetivo, Estado, indica si un proceso es problemático (1) o no (0), basada en el uso de recursos, número de errores, tipo de proceso, y otros factores relevantes.
Tareas a Realizar¶
Análisis Exploratorio de Datos (EDA): Realice un análisis preliminar para entender la distribución y las características de los datos. Esto incluye verificar valores faltantes, la distribución de las variables numéricas, y la frecuencia de las categorías en variables categóricas.
Preprocesamiento de Datos:
- Limpieza: Maneje valores faltantes y elimine duplicados si los hay.
- Codificación de variables categóricas: Utilice técnicas como One-Hot Encoding para convertir
Tipo_Procesoen variables numéricas. - Escalado de características: Normalice o escale las características numéricas para que tengan el mismo rango de valores, lo cual es importante para algunos modelos de clasificación.
Selección y División del Conjunto de Datos: Divida el conjunto de datos en entrenamiento, validación y prueba para evaluar la efectividad del modelo.
Construcción y Evaluación de Modelos:
- Pruebe diferentes algoritmos de clasificación (por ejemplo, Regresión Logística, Árboles de Decisión, Bosques Aleatorios, y Máquinas de Soporte Vectorial) para encontrar el más adecuado.
- Utilice validación cruzada para optimizar los hiperparámetros y evaluar la robustez del modelo.
- Evalúe el modelo final utilizando el conjunto de prueba y métricas adecuadas (precisión, recall, puntuación F1, y curva ROC).
Interpretación de Resultados y Conclusiones: Interprete los resultados del modelo en el contexto del problema. Identifique las características más importantes que influyen en la predicción de procesos problemáticos y discuta cómo se puede utilizar el modelo en un entorno de producción.
Reporte: Prepare un informe detallado que cubra todos los pasos realizados, desde el EDA hasta la interpretación de los resultados, incluyendo cualquier insight relevante obtenido durante el proceso.
Entrega¶
La tarea final consistirá en un Jupyter Notebook que contenga todo el código utilizado para el análisis, preprocesamiento de datos, modelado, y evaluación, junto con un reporte escrito (dentro del mismo notebook o como un documento separado) que resuma los hallazgos y las recomendaciones basadas en el modelo desarrollado.
1. Análisis exporatorio de Datos¶
Realice un análisis preliminar para entender la distribución y las características de los datos. Esto incluye verificar valores faltantes, la distribución de las variables numéricas, y la frecuencia de las categorías en variables categóricas
import os
import numpy as np
import pandas as pd
import warnings
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.express as px
warnings.filterwarnings("ignore")
pd.set_option("display.max_rows",None)
from sklearn import preprocessing
import matplotlib
matplotlib.style.use('ggplot')
from sklearn.preprocessing import LabelEncoder
df=pd.read_csv("datos_procesos.csv", sep= '|')
df.head()
| ID_Proceso | Uso_CPU | Uso_Memoria | Numero_Hilos | Tiempo_Ejecucion | Numero_Errores | Tipo_Proceso | Estado | |
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 37.454012 | 59.515562 | 16 | 8.184879 | 3.0 | Aplicación | 0.0 |
| 1 | 2 | 95.071431 | 36.471714 | 18 | 76.195256 | 8.0 | Aplicación | 0.0 |
| 2 | 3 | 73.199394 | 0.537562 | 21 | 46.991138 | 3.0 | Aplicación | 0.0 |
| 3 | 4 | 59.865848 | 56.108773 | 28 | 87.884325 | 4.0 | Aplicación | 0.0 |
| 4 | 5 | 15.601864 | 89.657041 | 39 | 4.622599 | 1.0 | Sistema | 0.0 |
df.shape
(120768, 8)
df.dtypes
| 0 | |
|---|---|
| ID_Proceso | int64 |
| Uso_CPU | float64 |
| Uso_Memoria | float64 |
| Numero_Hilos | int64 |
| Tiempo_Ejecucion | float64 |
| Numero_Errores | float64 |
| Tipo_Proceso | object |
| Estado | float64 |
df["Estado"].value_counts()
#Data desbalanceada
| count | |
|---|---|
| Estado | |
| 0.0 | 112915 |
| 1.0 | 7852 |
string_col = df.select_dtypes(include="object").columns
df[string_col]=df[string_col].astype("string")
df.dtypes
| 0 | |
|---|---|
| ID_Proceso | int64 |
| Uso_CPU | float64 |
| Uso_Memoria | float64 |
| Numero_Hilos | int64 |
| Tiempo_Ejecucion | float64 |
| Numero_Errores | float64 |
| Tipo_Proceso | string[python] |
| Estado | float64 |
string_col=df.select_dtypes("string").columns.to_list()
string_col
['Tipo_Proceso']
num_col=df.columns.to_list()
#print(num_col)
for col in string_col:
num_col.remove(col)
num_col.remove("Estado")
num_col
['ID_Proceso', 'Uso_CPU', 'Uso_Memoria', 'Numero_Hilos', 'Tiempo_Ejecucion', 'Numero_Errores']
df.describe().T
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| ID_Proceso | 120768.0 | 60384.500000 | 34862.862992 | 1.000000 | 30192.750000 | 60384.500000 | 90576.250000 | 120768.000000 |
| Uso_CPU | 120768.0 | 49.963090 | 28.852428 | 0.000554 | 24.920219 | 50.071009 | 74.969747 | 99.999204 |
| Uso_Memoria | 120768.0 | 49.944844 | 28.858352 | 0.000051 | 25.006014 | 49.859720 | 74.924148 | 99.999778 |
| Numero_Hilos | 120768.0 | 25.030033 | 14.160240 | 1.000000 | 13.000000 | 25.000000 | 37.000000 | 49.000000 |
| Tiempo_Ejecucion | 120768.0 | 50.053274 | 28.824297 | 0.100374 | 25.121823 | 50.046113 | 75.073482 | 99.999985 |
| Numero_Errores | 120767.0 | 5.004115 | 2.234110 | 0.000000 | 3.000000 | 5.000000 | 6.000000 | 17.000000 |
| Estado | 120767.0 | 0.065018 | 0.246558 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 |
fig=px.histogram(df,
x="Numero_Errores",
color="Estado",
hover_data=df.columns,
title="Distribution: Numero de errores",
barmode="group")
fig.show()
fig=px.histogram(df,
x="Numero_Hilos",
color="Estado",
hover_data=df.columns,
title="Distribution: Numero de Hilos",
barmode="group")
fig.show()
Output hidden; open in https://colab.research.google.com to view.
fig=px.histogram(df,
x="Estado",
hover_data=df.columns,
title="Estado Ratio in the Data")
fig.show()
plt.figure(figsize=(15,10))
sns.pairplot(df,hue="Estado")
plt.title("Looking for Insites in Data")
plt.legend("Estado de funcionamiento")
plt.tight_layout()
plt.plot()
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) <ipython-input-17-c9598521d872> in <cell line: 2>() 1 plt.figure(figsize=(15,10)) ----> 2 sns.pairplot(df,hue="Estado") 3 plt.title("Looking for Insites in Data") 4 plt.legend("Estado de funcionamiento") 5 plt.tight_layout() /usr/local/lib/python3.10/dist-packages/seaborn/axisgrid.py in pairplot(data, hue, hue_order, palette, vars, x_vars, y_vars, kind, diag_kind, markers, height, aspect, corner, dropna, plot_kws, diag_kws, grid_kws, size) 2175 # Add a legend 2176 if hue is not None: -> 2177 grid.add_legend() 2178 2179 grid.tight_layout() /usr/local/lib/python3.10/dist-packages/seaborn/axisgrid.py in add_legend(self, legend_data, title, label_order, adjust_subtitles, **kwargs) 193 194 # Draw the plot to set the bounding boxes correctly --> 195 _draw_figure(self._figure) 196 197 # Calculate and set the new width of the figure so the legend fits /usr/local/lib/python3.10/dist-packages/seaborn/utils.py in _draw_figure(fig) 59 """Force draw of a matplotlib figure, accounting for back-compat.""" 60 # See https://github.com/matplotlib/matplotlib/issues/19197 for context ---> 61 fig.canvas.draw() 62 if fig.stale: 63 try: /usr/local/lib/python3.10/dist-packages/matplotlib/backends/backend_agg.py in draw(self) 386 with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar 387 else nullcontext()): --> 388 self.figure.draw(self.renderer) 389 # A GUI class may be need to update a window using this draw, so 390 # don't forget to call the superclass. /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 93 @wraps(draw) 94 def draw_wrapper(artist, renderer, *args, **kwargs): ---> 95 result = draw(artist, renderer, *args, **kwargs) 96 if renderer._rasterizing: 97 renderer.stop_rasterizing() /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/figure.py in draw(self, renderer) 3152 3153 self.patch.draw(renderer) -> 3154 mimage._draw_list_compositing_images( 3155 renderer, self, artists, self.suppressComposite) 3156 /usr/local/lib/python3.10/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 130 if not_composite or not has_images: 131 for a in artists: --> 132 a.draw(renderer) 133 else: 134 # Composite any adjacent images together /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/axes/_base.py in draw(self, renderer) 3068 _draw_rasterized(self.figure, artists_rasterized, renderer) 3069 -> 3070 mimage._draw_list_compositing_images( 3071 renderer, self, artists, self.figure.suppressComposite) 3072 /usr/local/lib/python3.10/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 130 if not_composite or not has_images: 131 for a in artists: --> 132 a.draw(renderer) 133 else: 134 # Composite any adjacent images together /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/collections.py in draw(self, renderer) 1003 def draw(self, renderer): 1004 self.set_sizes(self._sizes, self.figure.dpi) -> 1005 super().draw(renderer) 1006 1007 /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/collections.py in draw(self, renderer) 421 "screen") 422 --> 423 renderer.draw_path_collection( 424 gc, transform.frozen(), paths, 425 self.get_transforms(), offsets, offset_trf, /usr/local/lib/python3.10/dist-packages/matplotlib/path.py in vertices(self) 209 ) 210 --> 211 @property 212 def vertices(self): 213 """The vertices of the `Path` as an (N, 2) array.""" KeyboardInterrupt:
<Figure size 1500x1000 with 0 Axes>
Error in callback <function flush_figures at 0x7ece53a651b0> (for post_execute):
--------------------------------------------------------------------------- KeyboardInterrupt Traceback (most recent call last) /usr/local/lib/python3.10/dist-packages/matplotlib_inline/backend_inline.py in flush_figures() 124 # ignore the tracking, just draw and close all figures 125 try: --> 126 return show(True) 127 except Exception as e: 128 # safely show traceback if in IPython, else raise /usr/local/lib/python3.10/dist-packages/matplotlib_inline/backend_inline.py in show(close, block) 88 try: 89 for figure_manager in Gcf.get_all_fig_managers(): ---> 90 display( 91 figure_manager.canvas.figure, 92 metadata=_fetch_figure_metadata(figure_manager.canvas.figure) /usr/local/lib/python3.10/dist-packages/IPython/core/display.py in display(include, exclude, metadata, transient, display_id, *objs, **kwargs) 318 publish_display_data(data=obj, metadata=metadata, **kwargs) 319 else: --> 320 format_dict, md_dict = format(obj, include=include, exclude=exclude) 321 if not format_dict: 322 # nothing to display (e.g. _ipython_display_ took over) /usr/local/lib/python3.10/dist-packages/IPython/core/formatters.py in format(self, obj, include, exclude) 178 md = None 179 try: --> 180 data = formatter(obj) 181 except: 182 # FIXME: log the exception <decorator-gen-2> in __call__(self, obj) /usr/local/lib/python3.10/dist-packages/IPython/core/formatters.py in catch_format_error(method, self, *args, **kwargs) 222 """show traceback on failed format call""" 223 try: --> 224 r = method(self, *args, **kwargs) 225 except NotImplementedError: 226 # don't warn on NotImplementedErrors /usr/local/lib/python3.10/dist-packages/IPython/core/formatters.py in __call__(self, obj) 339 pass 340 else: --> 341 return printer(obj) 342 # Finally look for special method names 343 method = get_real_method(obj, self.print_method) /usr/local/lib/python3.10/dist-packages/IPython/core/pylabtools.py in print_figure(fig, fmt, bbox_inches, base64, **kwargs) 149 FigureCanvasBase(fig) 150 --> 151 fig.canvas.print_figure(bytes_io, **kw) 152 data = bytes_io.getvalue() 153 if fmt == 'svg': /usr/local/lib/python3.10/dist-packages/matplotlib/backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs) 2185 # force the figure dpi to 72), so we need to set it again here. 2186 with cbook._setattr_cm(self.figure, dpi=dpi): -> 2187 result = print_method( 2188 filename, 2189 facecolor=facecolor, /usr/local/lib/python3.10/dist-packages/matplotlib/backend_bases.py in <lambda>(*args, **kwargs) 2041 "bbox_inches_restore"} 2042 skip = optional_kws - {*inspect.signature(meth).parameters} -> 2043 print_method = functools.wraps(meth)(lambda *args, **kwargs: meth( 2044 *args, **{k: v for k, v in kwargs.items() if k not in skip})) 2045 else: # Let third-parties do as they see fit. /usr/local/lib/python3.10/dist-packages/matplotlib/backends/backend_agg.py in print_png(self, filename_or_obj, metadata, pil_kwargs) 495 *metadata*, including the default 'Software' key. 496 """ --> 497 self._print_pil(filename_or_obj, "png", pil_kwargs, metadata) 498 499 def print_to_buffer(self): /usr/local/lib/python3.10/dist-packages/matplotlib/backends/backend_agg.py in _print_pil(self, filename_or_obj, fmt, pil_kwargs, metadata) 443 *pil_kwargs* and *metadata* are forwarded). 444 """ --> 445 FigureCanvasAgg.draw(self) 446 mpl.image.imsave( 447 filename_or_obj, self.buffer_rgba(), format=fmt, origin="upper", /usr/local/lib/python3.10/dist-packages/matplotlib/backends/backend_agg.py in draw(self) 386 with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar 387 else nullcontext()): --> 388 self.figure.draw(self.renderer) 389 # A GUI class may be need to update a window using this draw, so 390 # don't forget to call the superclass. /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer, *args, **kwargs) 93 @wraps(draw) 94 def draw_wrapper(artist, renderer, *args, **kwargs): ---> 95 result = draw(artist, renderer, *args, **kwargs) 96 if renderer._rasterizing: 97 renderer.stop_rasterizing() /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/figure.py in draw(self, renderer) 3152 3153 self.patch.draw(renderer) -> 3154 mimage._draw_list_compositing_images( 3155 renderer, self, artists, self.suppressComposite) 3156 /usr/local/lib/python3.10/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 130 if not_composite or not has_images: 131 for a in artists: --> 132 a.draw(renderer) 133 else: 134 # Composite any adjacent images together /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/axes/_base.py in draw(self, renderer) 3068 _draw_rasterized(self.figure, artists_rasterized, renderer) 3069 -> 3070 mimage._draw_list_compositing_images( 3071 renderer, self, artists, self.figure.suppressComposite) 3072 /usr/local/lib/python3.10/dist-packages/matplotlib/image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite) 130 if not_composite or not has_images: 131 for a in artists: --> 132 a.draw(renderer) 133 else: 134 # Composite any adjacent images together /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/collections.py in draw(self, renderer) 1003 def draw(self, renderer): 1004 self.set_sizes(self._sizes, self.figure.dpi) -> 1005 super().draw(renderer) 1006 1007 /usr/local/lib/python3.10/dist-packages/matplotlib/artist.py in draw_wrapper(artist, renderer) 70 renderer.start_filter() 71 ---> 72 return draw(artist, renderer) 73 finally: 74 if artist.get_agg_filter() is not None: /usr/local/lib/python3.10/dist-packages/matplotlib/collections.py in draw(self, renderer) 421 "screen") 422 --> 423 renderer.draw_path_collection( 424 gc, transform.frozen(), paths, 425 self.get_transforms(), offsets, offset_trf, /usr/local/lib/python3.10/dist-packages/matplotlib/path.py in vertices(self) 209 ) 210 --> 211 @property 212 def vertices(self): 213 """The vertices of the `Path` as an (N, 2) array.""" KeyboardInterrupt:
fig = px.box(df,y="Uso_Memoria",x="Estado",title=f"Distrubucion del uso de memoria")
fig.show()
Output hidden; open in https://colab.research.google.com to view.
fig = px.box(df,y="Uso_CPU",x="Estado",title=f"Distrubucion del uso de memoria")
fig.show()
Output hidden; open in https://colab.research.google.com to view.
2. Preprocesamiento de Datos:¶
2.1 Limpieza de datos: Maneje valores faltantes y elimine duplicados si los hay.
# Checking for NULLs in the data
df.isnull().sum()
| 0 | |
|---|---|
| ID_Proceso | 0 |
| Uso_CPU | 0 |
| Uso_Memoria | 0 |
| Numero_Hilos | 0 |
| Tiempo_Ejecucion | 0 |
| Numero_Errores | 1 |
| Tipo_Proceso | 1 |
| Estado | 1 |
# Eliminar filas con valores nulos en columnas específicas
df_sin_nulos = df.dropna(subset=["Numero_Errores", "Tipo_Proceso", "Estado"])
df_sin_nulos.isnull().sum()
| 0 | |
|---|---|
| ID_Proceso | 0 |
| Uso_CPU | 0 |
| Uso_Memoria | 0 |
| Numero_Hilos | 0 |
| Tiempo_Ejecucion | 0 |
| Numero_Errores | 0 |
| Tipo_Proceso | 0 |
| Estado | 0 |
# Identificar las filas duplicadas
duplicated_rows = df_sin_nulos.duplicated(subset=["ID_Proceso"])
# Contar el número de filas duplicadas
num_duplicated_rows = duplicated_rows.sum()
# Mostrar el resultado
print(f"\nNúmero de filas duplicadas: {num_duplicated_rows}")
Número de filas duplicadas: 0
2.2 Codificación de variables categóricas: Utilice técnicas como One-Hot Encoding para convertir Tipo_Proceso en variables numéricas.
df_sin_nulos[string_col].head()
for col in string_col:
print(f"The distribution of categorical valeus in the {col} is : ")
print(df[col].value_counts())
The distribution of categorical valeus in the Tipo_Proceso is : Tipo_Proceso Servicio 40363 Aplicación 40314 Sistema 40090 Name: count, dtype: Int64
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
# Crear el codificador
encoder = OneHotEncoder(sparse_output=False)
# Aplicar One-Hot Encoding a la columna Tipo_Proceso
encoded_array = encoder.fit_transform(df_sin_nulos[["Tipo_Proceso"]])
# Crear un DataFrame con las columnas codificadas
encoded_df = pd.DataFrame(
encoded_array,
columns=encoder.get_feature_names_out(["Tipo_Proceso"])
)
# Concatenar el DataFrame original con las columnas codificadas
df_encoded = pd.concat([df_sin_nulos.drop("Tipo_Proceso", axis=1), encoded_df], axis=1)
df_encoded.head()
| ID_Proceso | Uso_CPU | Uso_Memoria | Numero_Hilos | Tiempo_Ejecucion | Numero_Errores | Estado | Tipo_Proceso_Aplicación | Tipo_Proceso_Servicio | Tipo_Proceso_Sistema | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 37.454012 | 59.515562 | 16 | 8.184879 | 3.0 | 0.0 | 1.0 | 0.0 | 0.0 |
| 1 | 2 | 95.071431 | 36.471714 | 18 | 76.195256 | 8.0 | 0.0 | 1.0 | 0.0 | 0.0 |
| 2 | 3 | 73.199394 | 0.537562 | 21 | 46.991138 | 3.0 | 0.0 | 1.0 | 0.0 | 0.0 |
| 3 | 4 | 59.865848 | 56.108773 | 28 | 87.884325 | 4.0 | 0.0 | 1.0 | 0.0 | 0.0 |
| 4 | 5 | 15.601864 | 89.657041 | 39 | 4.622599 | 1.0 | 0.0 | 0.0 | 0.0 | 1.0 |
2.3 Escalado de características: Normalice o escale las características numéricas para que tengan el mismo rango de valores, lo cual es importante para algunos modelos de clasificación.
import pandas as pd
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Seleccionar columnas numéricas (excluyendo ID si no es relevante)
columns_to_normalize = ["Uso_CPU", "Uso_Memoria", "Numero_Hilos","Tiempo_Ejecucion", "Numero_Errores"]
# Usar MinMaxScaler para normalizar
scaler = MinMaxScaler()
df_normalized = scaler.fit_transform(df_encoded[columns_to_normalize])
# Crear un DataFrame con los datos normalizados
df_normalized = pd.DataFrame(df_normalized, columns=columns_to_normalize)
# Reincorporar los datos normalizados al DataFrame original
df_combined = df_encoded.copy()
df_combined[columns_to_normalize] = df_normalized
# Eliminar columnas innecesarias (sin usar inplace)
df_combined = df_combined.drop(columns=["ID_Proceso"])
df_combined = df_combined.rename(columns={"Tipo_Proceso_Aplicación": "TP_Aplicacion",
"Tipo_Proceso_Servicio": "TP_Servicio",
"Tipo_Proceso_Sistema": "TP_Sistema"})
# Mostrar las primeras filas del DataFrame combinado
print(df_combined.head())
Uso_CPU Uso_Memoria Numero_Hilos Tiempo_Ejecucion Numero_Errores \ 0 0.374540 0.595157 0.312500 0.080926 0.176471 1 0.950722 0.364718 0.354167 0.761713 0.470588 2 0.731998 0.005375 0.416667 0.469379 0.176471 3 0.598661 0.561089 0.562500 0.878722 0.235294 4 0.156015 0.896572 0.791667 0.045268 0.058824 Estado TP_Aplicacion TP_Servicio TP_Sistema 0 0.0 1.0 0.0 0.0 1 0.0 1.0 0.0 0.0 2 0.0 1.0 0.0 0.0 3 0.0 1.0 0.0 0.0 4 0.0 0.0 0.0 1.0
3. Selección y División del Conjunto de Datos:¶
Divida el conjunto de datos en entrenamiento, validación y prueba para evaluar la efectividad del modelo.
4. Construcción y Evaluación de Modelos:¶
Pruebe diferentes algoritmos de clasificación (por ejemplo, Regresión Logística, Árboles de Decisión, Bosques Aleatorios, y Máquinas de Soporte Vectorial) para encontrar el más adecuado. Utilice validación cruzada para optimizar los hiperparámetros y evaluar la robustez del modelo. Evalúe el modelo final utilizando el conjunto de prueba y métricas adecuadas (precisión, recall, puntuación F1, y curva ROC).
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier, AdaBoostClassifier
import xgboost as xgb
import lightgbm as lgb
import shap
# Carga los datos
data = df_combined.copy()
X = data[["Uso_CPU", "Uso_Memoria", "Numero_Hilos","Tiempo_Ejecucion", "Numero_Errores","TP_Aplicacion","TP_Servicio","TP_Sistema"]]
y = data["Estado"]
# División de los datos en entrenamiento y prueba
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
# Entrenamiento y evaluación de los modelos
models = {
"Logistic Regression": LogisticRegression(),
"Decision Tree": DecisionTreeClassifier(),
"Random Forest": RandomForestClassifier(),
"Gradient Boosting": GradientBoostingClassifier(),
"AdaBoost": AdaBoostClassifier(),
"XGBoost": xgb.XGBClassifier(),
"LightGBM": lgb.LGBMClassifier()
}
results = pd.DataFrame(columns=["Accuracy", "Precision", "Recall", "F1 Score", "ROC AUC"])
for name, model in models.items():
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
precision = precision_score(y_test, y_pred)
recall = recall_score(y_test, y_pred)
f1 = f1_score(y_test, y_pred)
roc_auc = roc_auc_score(y_test, y_pred)
results.loc[name] = [accuracy, precision, recall, f1, roc_auc]
# Muestra los resultados
print(results)
[LightGBM] [Info] Number of positive: 5486, number of negative: 79050
[LightGBM] [Info] Auto-choosing row-wise multi-threading, the overhead of testing was 0.001980 seconds.
You can set `force_row_wise=true` to remove the overhead.
And if memory is not enough, you can set `force_col_wise=true`.
[LightGBM] [Info] Total Bins 838
[LightGBM] [Info] Number of data points in the train set: 84536, number of used features: 8
[LightGBM] [Info] [binary:BoostFromScore]: pavg=0.064895 -> initscore=-2.667881
[LightGBM] [Info] Start training from score -2.667881
Accuracy Precision Recall F1 Score ROC AUC
Logistic Regression 0.957467 0.730834 0.551986 0.628943 0.768892
Decision Tree 0.934531 0.498756 0.508453 0.503558 0.736376
Random Forest 0.954321 0.705136 0.516484 0.596243 0.750697
Gradient Boosting 0.956915 0.729869 0.540152 0.620840 0.763092
AdaBoost 0.953493 0.678179 0.547760 0.606032 0.764800
XGBoost 0.954321 0.684484 0.557481 0.614489 0.769764
LightGBM 0.955894 0.696520 0.575232 0.630093 0.778861
5. Interpretación de Resultados y Conclusiones:¶
Interprete los resultados del modelo en el contexto del problema. Identifique las características más importantes que influyen en la predicción de procesos problemáticos y discuta cómo se puede utilizar el modelo en un entorno de producción.
explainer = shap.Explainer(models["Random Forest"].predict, X_train)
shap_values = explainer(X_test)
shap.summary_plot(shap_values, X_test)
ExactExplainer explainer: 36232it [59:22, 10.15it/s]
shap.plots.bar(shap_values)
Es posible observar que hay variables que tienen un peso considerable en la probabilidad de predecir una problemática en un proceso, las cuales son: \
1.-Uso_CPU \ 2.-Uso_Memoria \ 3.-TP_Sistema \ 4.-Tiempo_Ejecucion \
Esto quiere decir que el uso de CPU, el uso de la memoria y el tiempo de ejecuión son variables que se deben monitorear para alarmar al usuario de que habrá una probable falla y que habrá una gran probabilidad de que esté relacionada con Tipo de proceso que tenga que ver con el sistema. \
Si usamos este modelo nos proporciona una herramienta útil para predecir problemas en procesos del sistema, basándose en características clave como el uso de recursos y el tiempo de ejecución. Al implementarlo en un entorno de producción, puede mejorar la estabilidad y el rendimiento del servidor al detectar y mitigar problemas de manera proactiva. Sin embargo, debe ser monitoreado continuamente y ajustado para reflejar los cambios en el entorno del servidor.
6. Reporte:¶
Prepare un informe detallado que cubra todos los pasos realizados, desde el EDA hasta la interpretación de los resultados, incluyendo cualquier insight relevante obtenido durante el proceso.
Nuestro objetivo en este notebook es desarrollar un modelo de clasificación que pueda predecir si un proceso en ejecución en un servidor causará problemas, basándose en datos históricos de los procesos. El modelo ayudará en la detección temprana de procesos potencialmente problemáticos, permitiendo tomar acciones preventivas para asegurar la estabilidad y el rendimiento del servidor.
1. Análisis exploratorio:¶
Indagando en nuestra base de datos encontramos una forma dimensional (120768, 8) que representan 8 columnas y 120768 filas. De las 8 columnas todas entraban en variables de tipo entero INT64 o tipo flotant FLOAT64 a excepción del tipo de proceso que era de tipo objeto, por lo que se tuvo que transformar a una variable de tipo string STR para hacer más eficientes los algoritmos dentro de la máquina.
También se encontró que nuestra base de datos se encontraba desbalanceada. Siendo la distribución como sigue:
[ \begin{array}{|c|c|} \hline \textbf{Estado} & \textbf{Count} \\ \hline 0.0 & 112915 \\ 1.0 & 7852 \\ \hline \end{array} ]
Donde recordemos el Estado 0 inidica que no hay un proceso problemático y el Estado 1 que si lo hay. En nuestra distribución de errores encontramos que la media se centra en los 5 errores. Es ahí donde aparecen más procesos problemáticos y donde se encuentran más procesos sin fallo. Cuando uno se centra en la distribución de hilos fallidos uno se da cuenta que hay una correlación donde al aumentar el número de hilos aumentan la cantidad de procesos fallidos. Asímismo es posible ver que hay una alta correlación entre el uso tanto de memoria como de CPU para incrementar los procesos problemáticos.
2.- Procesamiento de Datos¶
2.1.1 Limpieza de datos: Eliminar valores nulos¶
El primer paso para limpiar nuestra data fue verificar si habían datos nulos Nan, y resulto que las filas Numero de errores, Tipo de Proceso y Estado tenían valores nulos. Por lo que se procedio a eliminar las correspondientes filas con el método dropna.
2.1.2 Eliminar filas duplicadas¶
Seguido de esto se verifico si habían columnas que estaban duplicadas. Después de aplicar el algoritmo se registraron cero filas duplicadas, por lo que no fue necesario eliminar filas que estuviesen duplicadas.
2.2 Codificación de variables categóricas¶
2.2.1 Uso de One-Hot encoding¶
Al mandar a llamar la libreria sklearn.preprocessing e importando OneHotEncoder es posible trabajar con variables categóricas, lo que hace es transformar a binarios nuestras caategorias de Tipo de Proceso involucradas: Aplicación, Servicio y Sistema. Se crean columnas independientes para cada categoría y se asigna un 1 si pertenece a esa categoría y un cero si no. Después de este proces al script se le pude unir esta tabla a la original para tener así una tabla con valores numéricos que puedan ser interpretados. El problema de OneHotEncoder es que aumenta las dimensiones del problema y representa un consto computacional mayor.
2.3 Escalado de características¶
El escalado de características se refiere al proceso de normalización de nuestra data. Hay campos que tienen registros con números muy grandes respecto a los ofrecidos por otros campos, por lo que al hacer visualizaciones y análisis numérico los datos se se sesgan inclinandose por los números más grandes y no es posible dar una lectura legible de los mismos. Por eso, es conveniente trabajar con un dataset normalizado. Las maneras más comunes de normalizar con los métodos de sklearn.preprocessing que es la nomalización estándar StandardScaler y la normalización por el método de mínimos y máximos MinMaxScaler. En nuestro caso particular escogimos la última, aunque la normalización estándar es más común. Cabe resaltar que en modelos de árbol no es necesario normalizar por el tipo de algoritmo por el que funcionan. \
En nuestro algoritmo de normalización solo escogimos las variables que aportan valor analítico, por lo que tuvimos que excluir al ID del proceso. Por otra parte, nuestras variables a normalizar y nuestras futuras candidatas para entrenar nuestro modelo fueron:\
columns_to_normalize = ["Uso_CPU", "Uso_Memoria", "Numero_Hilos","Tiempo_Ejecucion", "Numero_Errores"] \
Después de aplicar neustra transformación de escala se unieron nuestra columnas normalizada con las columnas codificadas para así ya tener nuestro dataframe preprocesao para pasar a entrenar la data con disitntos modelos.
En las secciones 3 y 4 simplemente se contruyo el algoritmo para divir nuestra data en un porcentaje de entrenamiento (70%) y otro de prueba (30%), para poder ver la capacidad de predicción de distintos modelos de predicción de machine learning, todo usano la semilla 0 (random_state):
models = {
"Logistic Regression": LogisticRegression(),
"Decision Tree": DecisionTreeClassifier(),
"Random Forest": RandomForestClassifier(),
"Gradient Boosting": GradientBoostingClassifier(),
"AdaBoost": AdaBoostClassifier(),
"XGBoost": xgb.XGBClassifier(),
"LightGBM": lgb.LGBMClassifier()
}
Los anteriores modelos tendrían que entrenarse con métricas que funcionan a partir de las predicciones que fueron correctas con las que realmente fueron correctas, por ejemplo. Entre las métricas se encuentran: \
"Accuracy", "Precision", "Recall", "F1 Score", "ROC AUC"
RESULTADOS¶
Interpretación de Métricas¶
Accuracy:¶
Una precisión alta (~0.95) indica que el modelo clasifica correctamente la mayoría de los procesos como problemáticos o no problemáticos. Sin embargo, no debe ser la única métrica evaluada, ya que un dataset desbalanceado podría inflar artificialmente la precisión. \
Precision:¶
Refleja qué proporción de los procesos predichos como problemáticos realmente lo son. Una alta precisión (~0.73) indica que el modelo es confiable en cuanto a los positivos identificados, lo que minimiza interrupciones innecesarias en procesos saludables. \
Recall:¶
Mide cuántos de los procesos realmente problemáticos son detectados por el modelo. Si esta métrica es baja (~0.56), algunos procesos problemáticos no serán detectados, lo que puede ser crítico en un entorno de servidor. \
F1 Score:¶
La media armónica entre precisión y recall sugiere un balance aceptable (~0.62). \
ROC AUC:¶
Un AUC cercano a 0.78 indica que el modelo tiene buena capacidad para distinguir entre procesos problemáticos y no problemáticos.
5. Interpretación de Resultados y Conclusiones:¶
Nuestro modelo predijo que hay 4 variables que tienen más peso en la probabilidad de predecir fallos en los procesos.
Uso_CPU:¶
Procesos con un uso alto de CPU son más propensos a ser problemáticos si consumen recursos en exceso.
Uso_Memoria:¶
Procesos que consumen mucha memoria podrían causar problemas de estabilidad, especialmente en sistemas con recursos limitados.
Tiempo_Ejecucion:¶
Procesos que han estado ejecutándose por períodos prolongados podrían causar acumulación de errores o consumir recursos de manera no óptima.
TP_Sistema:¶
Los procesos de tipo "Servicio" o "Sistema" podrían estar más relacionados con problemas críticos en el servidor.
IMPLEMENTACIÓN DEL MODELO¶
Un modelo así podría ser usado para la detección temprana de procesos problemáticos en modelos de tiempo real, por lo que serviría como un sistema de alertas del sistema para tomar medidas preventivas como reiniciar procesos, redistribuir carga, asignar más recursos al sistema. También puede ser utilizada para automatizar las respuestas de acción para que en lugar del usuario, el servidor tome acciones que no interrumpan el funcionamiendo del mismo. Aunque esa automatización corre el riesgo de perdida de información o de procesos.
!jupyter nbconvert --to html "/content/drive/My Drive/carpeta/mi_archivo.ipynb
Codigo Base Tarea.ipynb"
[NbConvertApp] WARNING | pattern 'Codigo Base Tarea.ipynb' matched no files
This application is used to convert notebook files (*.ipynb)
to various other formats.
WARNING: THE COMMANDLINE INTERFACE MAY CHANGE IN FUTURE RELEASES.
Options
=======
The options below are convenience aliases to configurable class-options,
as listed in the "Equivalent to" description-line of the aliases.
To see all configurable class-options for some <cmd>, use:
<cmd> --help-all
--debug
set log level to logging.DEBUG (maximize logging output)
Equivalent to: [--Application.log_level=10]
--show-config
Show the application's configuration (human-readable format)
Equivalent to: [--Application.show_config=True]
--show-config-json
Show the application's configuration (json format)
Equivalent to: [--Application.show_config_json=True]
--generate-config
generate default config file
Equivalent to: [--JupyterApp.generate_config=True]
-y
Answer yes to any questions instead of prompting.
Equivalent to: [--JupyterApp.answer_yes=True]
--execute
Execute the notebook prior to export.
Equivalent to: [--ExecutePreprocessor.enabled=True]
--allow-errors
Continue notebook execution even if one of the cells throws an error and include the error message in the cell output (the default behaviour is to abort conversion). This flag is only relevant if '--execute' was specified, too.
Equivalent to: [--ExecutePreprocessor.allow_errors=True]
--stdin
read a single notebook file from stdin. Write the resulting notebook with default basename 'notebook.*'
Equivalent to: [--NbConvertApp.from_stdin=True]
--stdout
Write notebook output to stdout instead of files.
Equivalent to: [--NbConvertApp.writer_class=StdoutWriter]
--inplace
Run nbconvert in place, overwriting the existing notebook (only
relevant when converting to notebook format)
Equivalent to: [--NbConvertApp.use_output_suffix=False --NbConvertApp.export_format=notebook --FilesWriter.build_directory=]
--clear-output
Clear output of current file and save in place,
overwriting the existing notebook.
Equivalent to: [--NbConvertApp.use_output_suffix=False --NbConvertApp.export_format=notebook --FilesWriter.build_directory= --ClearOutputPreprocessor.enabled=True]
--coalesce-streams
Coalesce consecutive stdout and stderr outputs into one stream (within each cell).
Equivalent to: [--NbConvertApp.use_output_suffix=False --NbConvertApp.export_format=notebook --FilesWriter.build_directory= --CoalesceStreamsPreprocessor.enabled=True]
--no-prompt
Exclude input and output prompts from converted document.
Equivalent to: [--TemplateExporter.exclude_input_prompt=True --TemplateExporter.exclude_output_prompt=True]
--no-input
Exclude input cells and output prompts from converted document.
This mode is ideal for generating code-free reports.
Equivalent to: [--TemplateExporter.exclude_output_prompt=True --TemplateExporter.exclude_input=True --TemplateExporter.exclude_input_prompt=True]
--allow-chromium-download
Whether to allow downloading chromium if no suitable version is found on the system.
Equivalent to: [--WebPDFExporter.allow_chromium_download=True]
--disable-chromium-sandbox
Disable chromium security sandbox when converting to PDF..
Equivalent to: [--WebPDFExporter.disable_sandbox=True]
--show-input
Shows code input. This flag is only useful for dejavu users.
Equivalent to: [--TemplateExporter.exclude_input=False]
--embed-images
Embed the images as base64 dataurls in the output. This flag is only useful for the HTML/WebPDF/Slides exports.
Equivalent to: [--HTMLExporter.embed_images=True]
--sanitize-html
Whether the HTML in Markdown cells and cell outputs should be sanitized..
Equivalent to: [--HTMLExporter.sanitize_html=True]
--log-level=<Enum>
Set the log level by value or name.
Choices: any of [0, 10, 20, 30, 40, 50, 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL']
Default: 30
Equivalent to: [--Application.log_level]
--config=<Unicode>
Full path of a config file.
Default: ''
Equivalent to: [--JupyterApp.config_file]
--to=<Unicode>
The export format to be used, either one of the built-in formats
['asciidoc', 'custom', 'html', 'latex', 'markdown', 'notebook', 'pdf', 'python', 'qtpdf', 'qtpng', 'rst', 'script', 'slides', 'webpdf']
or a dotted object name that represents the import path for an
``Exporter`` class
Default: ''
Equivalent to: [--NbConvertApp.export_format]
--template=<Unicode>
Name of the template to use
Default: ''
Equivalent to: [--TemplateExporter.template_name]
--template-file=<Unicode>
Name of the template file to use
Default: None
Equivalent to: [--TemplateExporter.template_file]
--theme=<Unicode>
Template specific theme(e.g. the name of a JupyterLab CSS theme distributed
as prebuilt extension for the lab template)
Default: 'light'
Equivalent to: [--HTMLExporter.theme]
--sanitize_html=<Bool>
Whether the HTML in Markdown cells and cell outputs should be sanitized.This
should be set to True by nbviewer or similar tools.
Default: False
Equivalent to: [--HTMLExporter.sanitize_html]
--writer=<DottedObjectName>
Writer class used to write the
results of the conversion
Default: 'FilesWriter'
Equivalent to: [--NbConvertApp.writer_class]
--post=<DottedOrNone>
PostProcessor class used to write the
results of the conversion
Default: ''
Equivalent to: [--NbConvertApp.postprocessor_class]
--output=<Unicode>
Overwrite base name use for output files.
Supports pattern replacements '{notebook_name}'.
Default: '{notebook_name}'
Equivalent to: [--NbConvertApp.output_base]
--output-dir=<Unicode>
Directory to write output(s) to. Defaults
to output to the directory of each notebook. To recover
previous default behaviour (outputting to the current
working directory) use . as the flag value.
Default: ''
Equivalent to: [--FilesWriter.build_directory]
--reveal-prefix=<Unicode>
The URL prefix for reveal.js (version 3.x).
This defaults to the reveal CDN, but can be any url pointing to a copy
of reveal.js.
For speaker notes to work, this must be a relative path to a local
copy of reveal.js: e.g., "reveal.js".
If a relative path is given, it must be a subdirectory of the
current directory (from which the server is run).
See the usage documentation
(https://nbconvert.readthedocs.io/en/latest/usage.html#reveal-js-html-slideshow)
for more details.
Default: ''
Equivalent to: [--SlidesExporter.reveal_url_prefix]
--nbformat=<Enum>
The nbformat version to write.
Use this to downgrade notebooks.
Choices: any of [1, 2, 3, 4]
Default: 4
Equivalent to: [--NotebookExporter.nbformat_version]
Examples
--------
The simplest way to use nbconvert is
> jupyter nbconvert mynotebook.ipynb --to html
Options include ['asciidoc', 'custom', 'html', 'latex', 'markdown', 'notebook', 'pdf', 'python', 'qtpdf', 'qtpng', 'rst', 'script', 'slides', 'webpdf'].
> jupyter nbconvert --to latex mynotebook.ipynb
Both HTML and LaTeX support multiple output templates. LaTeX includes
'base', 'article' and 'report'. HTML includes 'basic', 'lab' and
'classic'. You can specify the flavor of the format used.
> jupyter nbconvert --to html --template lab mynotebook.ipynb
You can also pipe the output to stdout, rather than a file
> jupyter nbconvert mynotebook.ipynb --stdout
PDF is generated via latex
> jupyter nbconvert mynotebook.ipynb --to pdf
You can get (and serve) a Reveal.js-powered slideshow
> jupyter nbconvert myslides.ipynb --to slides --post serve
Multiple notebooks can be given at the command line in a couple of
different ways:
> jupyter nbconvert notebook*.ipynb
> jupyter nbconvert notebook1.ipynb notebook2.ipynb
or you can specify the notebooks list in a config file, containing::
c.NbConvertApp.notebooks = ["my_notebook.ipynb"]
> jupyter nbconvert --config mycfg.py
To see all available configurables, use `--help-all`.